'use strict';

const { Controller } = require('egg');
const crypto = require("crypto");
// 引入微信配置参数（上面准备工作中保存的config/wx.js文件，包含小程序id，密钥，商户id，商户密钥）
const config = require("../../config/wxH5");
const fs = require("fs");
const path = require('path');
const dayjs = require("dayjs");
function generateNonceStr(len) {
  let data = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
  let str = "";
  for (let i = 0; i < len; i++) {
    str += data.charAt(Math.floor(Math.random() * data.length));
  }
  return str;
}

class RouterController extends Controller {
  async pay() {
    const { order_no } = this.ctx.request.body;
    const order = await this.ctx.model.OrderGoods.findOne({
      where: {
        order_no
      },
      include: [
        { model: this.ctx.model.Member }
      ]
    })
    if (!order) {
      throw new Error("订单不存在");
    }
    if (!order.member) {
      throw new Error("用户不存在");
    }
    const { pay_amount, desc } = order;
    const { h5_openid: openid } = order.member;
    if (!openid) {
      throw new Error("用户openid不存在");
    }
    const res = await this.toPay({
      pay_amount: pay_amount,
      desc: '服务订单',
      order_no,
      openid,
      notify_url: config.notify_url
    })
    console.log('下单参数', res)
    this.ctx.success({
      ...res,
      appid: config.appid
    });
  }
  async payRecharge() {
    const { order_no, pay_amount, member_id } = this.ctx.request.body;
    const member = await this.ctx.model.Member.findOne({
      where: {
        id: member_id
      }
    })
    if (!member) {
      throw new Error("用户不存在");
    }
    const { h5_openid: openid } = member;
    if (!openid) {
      throw new Error("用户openid不存在");
    }
    const res = await this.toPay({
      pay_amount: pay_amount,
      desc: '充值',
      order_no,
      openid,
      notify_url: config.notify_url_recharge
    })
    await this.ctx.model.OrderGoods.update({
      status: 2
    }, {
      where: {
        order_no
      }
    })
    this.ctx.success({
      ...res
    });
  }
  async toPay({ pay_amount, desc, order_no, openid, notify_url }) {
    console.log('config.notify_url',config.notify_url)
    // 生成订单号（保证唯一性：我采用时间戳拼6位随机数的方式）
    const createOrderParams = {
      appid: config.appid,
      mchid: config.mch_id,
      description: desc,
      out_trade_no: order_no,
      time_expire: new Date(Date.now() + 1000 * 60 * 30).toISOString(),
      notify_url,
      amount: {
        "total" : Math.ceil(pay_amount * 100),
        "currency" : "CNY"
      },
      payer: {
        openid
      }
    }
    let timeStamp = Math.floor(new Date().getTime() / 1000)
    let nonceStr = generateNonceStr(32)
    const Authorization = `WECHATPAY2-SHA256-RSA2048 mchid="${config.mch_id}",nonce_str="${nonceStr}",signature="${this.createOrderSign('POST',
        '/v3/pay/transactions/jsapi',
        timeStamp,
        nonceStr,
        createOrderParams
    )}",timestamp="${timeStamp}",serial_no="${config.serial_no}"`
    // 下单获取prepay_id
    const createPayRes = await this.ctx.curl(
        "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",
        {
          method: "post",
          data: createOrderParams,
          headers: {
            Authorization: Authorization,
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          dataType: 'json'
        }
    );
    if (createPayRes.data.prepay_id) {
      // 把参数+签名返回给前端
      const result = this.createPaySign(createPayRes.data.prepay_id)

      return {
        ...result,
        order_no
      }
    } else {
      throw new Error(createPayRes.data.message);
    }
  }
  /**
   * 微信支付v3 下单签名值生成
   * @param {string} method 请求方法
   * @param {string} url  微信小程序下单官方api
   * @param {number} timestamp 时间戳 秒级
   * @param {string} nonce_str 随机字符串
   * @param {Object} order 主体（订单）信息
   */
  createOrderSign(method, url, timestamp, nonce_str, order) {
    // 签名串
    let signStr = `${method}\n${url}\n${timestamp}\n${nonce_str}\n${JSON.stringify(
        order
    )}\n`;
    // 读取API证书文件内容 apiclient_key.pem的内容
    const filepath = path.join('app', '/public/apiclient_key.pem');
    let cert = fs.readFileSync(filepath, "utf-8");
    // 创建使用 RSA 算法和 SHA-256 散列算法的签名对象
    let sign = crypto.createSign("RSA-SHA256");
    // 对签名串进行加密处理
    sign.update(signStr);
    return sign.sign(cert, "base64");
  }

  createPaySign(prepay_id) {
    let timeStamp = Math.floor(new Date().getTime() / 1000)
    let nonceStr = generateNonceStr(32);
    let signStr = `${config.appid}\n${timeStamp}\n${nonceStr}\nprepay_id=${prepay_id}\n`;
    const filepath = path.join('app', '/public/apiclient_key.pem');
    let cert = fs.readFileSync(filepath, "utf-8");
    let sign = crypto.createSign("RSA-SHA256");
    sign.update(signStr);
    let paySign = sign.sign(cert, "base64");
    return {
      paySign: paySign,
      timestamp: timeStamp + '',
      nonce_str: nonceStr,
      signType: 'RSA',
      package: 'prepay_id=' + prepay_id
    };
  }

  // 微信支付回调地址
  async decodePayNotify(resource) {
    try {
      const AUTH_KEY_LENGTH = 16;
      // ciphertext = 密文，associated_data = 填充内容， nonce = 位移
      const { ciphertext, associated_data, nonce } = resource;
      // 密钥
      const key_bytes = Buffer.from(config.key, 'utf8');
      console.log('key_bytes',key_bytes)
      // 位移
      const nonce_bytes = Buffer.from(nonce, 'utf8');
      // 填充内容
      const associated_data_bytes = Buffer.from(associated_data, 'utf8');
      // 密文Buffer
      const ciphertext_bytes = Buffer.from(ciphertext, 'base64');
      // 计算减去16位长度
      const cipherdata_length = ciphertext_bytes.length - AUTH_KEY_LENGTH;
      // upodata
      const cipherdata_bytes = ciphertext_bytes.slice(0, cipherdata_length);
      // tag
      const auth_tag_bytes = ciphertext_bytes.slice(cipherdata_length, ciphertext_bytes.length);
      const decipher = crypto.createDecipheriv(
          'aes-256-gcm', key_bytes, nonce_bytes
      );
      decipher.setAuthTag(auth_tag_bytes);
      decipher.setAAD(Buffer.from(associated_data_bytes));
      const output = Buffer.concat([
        decipher.update(cipherdata_bytes),
        decipher.final(),
      ]);
      // 解密后 转成 JSON 格式输出
      return JSON.parse(output.toString('utf8'));
    }
    catch (error){
      console.error('解密错误:', error);
      return null;
    }

  }

  /**
   * 微信支付回调
   * @param {Object} req - 请求对象，包含查询参数
   * url  https://qy.xxx.com/v1/payment/wx/success
   */
  async wxnotify() {
    try {
      const { ctx } = this;
      const result = ctx.request.body;
      console.log('支付回调',result)
      if (result.event_type === 'TRANSACTION.SUCCESS') {
        // 解密微信支付成功后的订单信息
        const deInfo = await this.decodePayNotify(result.resource)
        console.log('deInfo',deInfo)
        await this.ctx.model.OrderGoods.update({
          pay_status: 1,
          transaction_id: deInfo.transaction_id,
          pay_time: deInfo.success_time,
        },{
          where: {
            order_no: deInfo.out_trade_no
          }
        })
        // 支付成功
        this.ctx.success('success', 200);
      } else {
        // 支付失败
        this.ctx.error('支付回调错误', 500);
      }
    }
    catch (err) {
      this.ctx.error('支付回调错误', 500);
    }
  }
  async wxnotifyRecharge() {
    try {
      const { ctx } = this;
      const result = ctx.request.body;
      console.log('支付回调',result)
      if (result.event_type === 'TRANSACTION.SUCCESS') {
        // 解密微信支付成功后的订单信息
        const deInfo = await this.decodePayNotify(result.resource)
        console.log('deInfo',deInfo)
        await this.ctx.model.Recharge.update({
          transaction_id: deInfo.transaction_id,
        },{
          where: {
            order_no: deInfo.out_trade_no,
            type: 1
          }
        })
        // 支付成功
        this.ctx.success('success', 200);
      } else {
        // 支付失败
        this.ctx.error('支付回调错误', 500);
      }
    }
    catch (err) {
      this.ctx.error('支付回调错误', 500);
    }
  }
  async wxnotifyRefund() {
    try {
      const { ctx } = this;
      const result = ctx.request.body;
      console.log('退款回调',result)
      // 解密微信支付成功后的订单信息
      const deInfo = await this.decodePayNotify(result.resource)
      if (result.event_type === 'REFUND.SUCCESS') {
        await this.ctx.model.OrderGoods.update({
          pay_status: 4,
          refund_id: deInfo.refund_id,
        },{
          where: {
            order_no: deInfo.out_trade_no
          }
        })
        // 支付成功
        this.ctx.success('success', 200);
      } else if (result.event_type === 'REFUND.ABNORMAL'){
        await this.ctx.model.OrderGoods.update({
          pay_status: 3,
          refund_id: deInfo.refund_id,
        },{
          where: {
            order_no: deInfo.out_trade_no
          }
        })
        // 支付失败
        this.ctx.error('支付回调错误', 500);
      } else {
        // 支付失败
        this.ctx.error('支付回调错误', 500);
      }
    }
    catch (err) {
      this.ctx.error('支付回调错误', 500);
    }
  }
}

module.exports = RouterController;
